// 版权归Go作者所有。版权所有。
// 此源代码的使用受BSD样式的约束
// 可以在许可证文件中找到的许可证。

package ld

import (
	"bytes"
	"cmd/internal/objabi"
	"cmd/link/internal/loader"
	"cmd/link/internal/sym"
	"encoding/binary"
	"fmt"
	"io/ioutil"
	"math/bits"
	"path/filepath"
	"sort"
	"strings"
	"sync"
)

// 该文件处理与XCOFF文件生成相关的所有算法。
// 其中大多数是对cmd/link/internal/pe.go中的内容的改编
// as PE和XCOFF基于COFF文件。
// 生成的XCOFF文件为64位。

const (
	// 文件开头保留的总空间量
	// 用于文件头、辅助头和节头。
	// 可能会浪费一些。
	XCOFFHDRRESERVE = FILHSZ_64 + AOUTHSZ_EXEC64 + SCNHSZ_64*23

	// 基于dump-o，然后从32B四舍五入到64B
	// 匹配ppc64上最差情况下的elf文本节对齐方式。
	XCOFFSECTALIGN int64 = 64

	// XCOFF二进制文件的所有部分通常应该是位置独立的。
	// 但是，由于一些R_ADDR重新定位，这对于.text是不可能的
	// 内部罗达符号。
	// .data和.bss是位置独立的，因此它们的地址从无法访问的
	// 执行期间的段，以在出现错误时强制执行SEGFULT。
	XCOFFTEXTBASE = 0x100000000 // 文本地址开头
	XCOFFDATABASE = 0x200000000 // 数据地址起始
)

// 文件头
type XcoffFileHdr64 struct {
	Fmagic   uint16 // 目标机
	Fnscns   uint16 // 节数
	Ftimedat int32  // 文件创建的时间和日期
	Fsymptr  uint64 // 符号表开始的字节偏移量
	Fopthdr  uint16 // 可选标头中的字节数
	Fflags   uint16 // 旗帜
	Fnsyms   int32  // 符号表中的条目数
}

const (
	U64_TOCMAGIC = 0767 // AIX 64位XCOFF
)

// 描述对象文件类型的标志。
const (
	F_RELFLG    = 0x0001
	F_EXEC      = 0x0002
	F_LNNO      = 0x0004
	F_FDPR_PROF = 0x0010
	F_FDPR_OPTI = 0x0020
	F_DSA       = 0x0040
	F_VARPG     = 0x0100
	F_DYNLOAD   = 0x1000
	F_SHROBJ    = 0x2000
	F_LOADONLY  = 0x4000
)

// 辅助集管
type XcoffAoutHdr64 struct {
	Omagic      int16    // 标志-如果Vstamp为1，则忽略
	Ovstamp     int16    // 版本
	Odebugger   uint32   // 保留给调试器
	Otextstart  uint64   // 文本的虚拟地址
	Odatastart  uint64   // 数据的虚拟地址
	Otoc        uint64   // Toc地址
	Osnentry    int16    // 入口点的区段编号
	Osntext     int16    // 文本的节号
	Osndata     int16    // 数据的节号
	Osntoc      int16    // Toc的章节号
	Osnloader   int16    // 装载机的段号
	Osnbss      int16    // Bss的节号
	Oalgntext   int16    // 最大文本对齐
	Oalgndata   int16    // 最大数据对齐
	Omodtype    [2]byte  // 模块类型字段
	Ocpuflag    uint8    // 位标志-对象的CPU类型
	Ocputype    uint8    // 为CPU类型保留
	Otextpsize  uint8    // 请求的文本页大小
	Odatapsize  uint8    // 请求的数据页大小
	Ostackpsize uint8    // 请求的堆栈页大小
	Oflags      uint8    // 标志和TLS对齐
	Otsize      uint64   // 文本大小（字节）
	Odsize      uint64   // 数据大小（字节）
	Obsize      uint64   // Bss大小（字节）
	Oentry      uint64   // 入口点地址
	Omaxstack   uint64   // 允许的最大堆栈大小
	Omaxdata    uint64   // 允许的最大数据大小
	Osntdata    int16    // Tdata节的节号
	Osntbss     int16    // Tbss区段的区段编号
	Ox64flags   uint16   // 64位对象的附加标志
	Oresv3a     int16    // 含蓄的
	Oresv3      [2]int32 // 含蓄的
}

// 节头
type XcoffScnHdr64 struct {
	Sname    [8]byte // 节名
	Spaddr   uint64  // 物理地址
	Svaddr   uint64  // 虚拟地址
	Ssize    uint64  // 截面尺寸
	Sscnptr  uint64  // 原始数据的文件偏移量
	Srelptr  uint64  // 文件偏移到重新定位
	Slnnoptr uint64  // 文件到行号的偏移量
	Snreloc  uint32  // 迁移条目数
	Snlnno   uint32  // 行号条目的数量
	Sflags   uint32  // 旗帜
}

// 定义节类型的标志。
const (
	STYP_DWARF  = 0x0010
	STYP_TEXT   = 0x0020
	STYP_DATA   = 0x0040
	STYP_BSS    = 0x0080
	STYP_EXCEPT = 0x0100
	STYP_INFO   = 0x0200
	STYP_TDATA  = 0x0400
	STYP_TBSS   = 0x0800
	STYP_LOADER = 0x1000
	STYP_DEBUG  = 0x2000
	STYP_TYPCHK = 0x4000
	STYP_OVRFLO = 0x8000
)
const (
	SSUBTYP_DWINFO  = 0x10000 // 矮人信息组
	SSUBTYP_DWLINE  = 0x20000 // 矮线号区段
	SSUBTYP_DWPBNMS = 0x30000 // 人名组
	SSUBTYP_DWPBTYP = 0x40000 // 公共类型组
	SSUBTYP_DWARNGE = 0x50000 // 矮亚兰科
	SSUBTYP_DWABREV = 0x60000 // 缩略语部分
	SSUBTYP_DWSTR   = 0x70000 // 矮弦剖面
	SSUBTYP_DWRNGES = 0x80000 // 矮岭组
	SSUBTYP_DWLOC   = 0x90000 // 矮人位置列表部分
	SSUBTYP_DWFRAME = 0xA0000 // 矮框架截面
	SSUBTYP_DWMAC   = 0xB0000 // 矮宏段
)

// 标题大小
const (
	FILHSZ_32      = 20
	FILHSZ_64      = 24
	AOUTHSZ_EXEC32 = 72
	AOUTHSZ_EXEC64 = 120
	SCNHSZ_32      = 40
	SCNHSZ_64      = 72
	LDHDRSZ_32     = 32
	LDHDRSZ_64     = 56
	LDSYMSZ_64     = 24
	RELSZ_64       = 14
)

// 表示所有XCOFF符号的类型。
type xcoffSym interface {
}

// 符号表条目
type XcoffSymEnt64 struct {
	Nvalue  uint64 // 符号值
	Noffset uint32 // 字符串表或.debug节中名称的偏移量
	Nscnum  int16  // 符号的节号
	Ntype   uint16 // 基本和派生类型规范
	Nsclass uint8  // 符号的存储类
	Nnumaux int8   // 辅助条目数
}

const SYMESZ = 18

const (
	// Nscnum
	N_DEBUG = -2
	N_ABS   = -1
	N_UNDEF = 0

	// 安提普
	SYM_V_INTERNAL  = 0x1000
	SYM_V_HIDDEN    = 0x2000
	SYM_V_PROTECTED = 0x3000
	SYM_V_EXPORTED  = 0x4000
	SYM_TYPE_FUNC   = 0x0020 // 是功能
)

// 存储类。
const (
	C_NULL    = 0   // 标记为删除的符号表条目
	C_EXT     = 2   // 外部符号
	C_STAT    = 3   // 静态符号
	C_BLOCK   = 100 // 内部块的开始或结束
	C_FCN     = 101 // 功能的开始或结束
	C_FILE    = 103 // 源文件名和编译器信息
	C_HIDEXT  = 107 // 未命名外部符号
	C_BINCL   = 108 // 包含文件的开头
	C_EINCL   = 109 // 包含文件结束
	C_WEAKEXT = 111 // 弱外部符号
	C_DWARF   = 112 // 矮人符号
	C_GSYM    = 128 // 全局变量
	C_LSYM    = 129 // 自动在堆栈上分配变量
	C_PSYM    = 130 // 堆栈上分配的子例程的参数
	C_RSYM    = 131 // 寄存器变量
	C_RPSYM   = 132 // 寄存器中存储的函数或过程的参数
	C_STSYM   = 133 // 静态分配符号
	C_BCOMM   = 135 // 公共块的开始
	C_ECOML   = 136 // 公共块的局部成员
	C_ECOMM   = 137 // 公共块的末尾
	C_DECL    = 140 // 对象声明
	C_ENTRY   = 141 // 备用条目
	C_FUN     = 142 // 功能或程序
	C_BSTAT   = 143 // 静态块的开始
	C_ESTAT   = 144 // 静态块结束
	C_GTLS    = 145 // 全局线程局部变量
	C_STTLS   = 146 // 静态线程局部变量
)

// 文件辅助条目
type XcoffAuxFile64 struct {
	Xzeroes  uint32 // 名称始终在字符串表中
	Xoffset  uint32 // 字符串表中的偏移量
	X_pad1   [6]byte
	Xftype   uint8 // 源文件字符串类型
	X_pad2   [2]byte
	Xauxtype uint8 // 辅助入口类型
}

// 功能辅助项
type XcoffAuxFcn64 struct {
	Xlnnoptr uint64 // 指向行号的文件指针
	Xfsize   uint32 // 函数的大小（以字节为单位）
	Xendndx  uint32 // 下一项的符号表索引
	Xpad     uint8  // 未使用
	Xauxtype uint8  // 辅助入口类型
}

// csect辅助入口。
type XcoffAuxCSect64 struct {
	Xscnlenlo uint32 // 长度或符号表索引的低位4字节
	Xparmhash uint32 // 参数类型检查字符串的偏移量
	Xsnhash   uint16 // .typchk段号
	Xsmtyp    uint8  // 符号对齐和类型
	Xsmclas   uint8  // 存储映射类
	Xscnlenhi uint32 // 长度或符号表索引的上4字节
	Xpad      uint8  // 未使用
	Xauxtype  uint8  // 辅助入口类型
}

// 矮人辅助入口
type XcoffAuxDWARF64 struct {
	Xscnlen  uint64 // 此符号段的长度
	X_pad    [9]byte
	Xauxtype uint8 // 辅助入口类型
}

// 辅助类型
const (
	_AUX_EXCEPT = 255
	_AUX_FCN    = 254
	_AUX_SYM    = 253
	_AUX_FILE   = 252
	_AUX_CSECT  = 251
	_AUX_SECT   = 250
)

// Xftype字段
const (
	XFT_FN = 0   // 源文件名
	XFT_CT = 1   // 编译时间戳
	XFT_CV = 2   // 编译器版本号
	XFT_CD = 128 // 编译器定义的信息/

)

// 符号类型字段。
const (
	XTY_ER  = 0    // 外部参照
	XTY_SD  = 1    // 节定义
	XTY_LD  = 2    // 标签定义
	XTY_CM  = 3    // 通用csect定义
	XTY_WK  = 0x8  // 弱符号
	XTY_EXP = 0x10 // 导出符号
	XTY_ENT = 0x20 // 入口点符号
	XTY_IMP = 0x40 // 输入符号
)

// 存储映射类。
const (
	XMC_PR     = 0  // 程序代码
	XMC_RO     = 1  // 只读常数
	XMC_DB     = 2  // 调试字典表
	XMC_TC     = 3  // TOC条目
	XMC_UA     = 4  // 未分类
	XMC_RW     = 5  // 读/写数据
	XMC_GL     = 6  // 全球联系
	XMC_XO     = 7  // 扩展操作
	XMC_SV     = 8  // 32位主管调用描述符
	XMC_BS     = 9  // BSS类
	XMC_DS     = 10 // 函数描述符
	XMC_UC     = 11 // 未命名的FORTRAN通用语言
	XMC_TC0    = 15 // TOC锚
	XMC_TD     = 16 // TOC中的标量数据条目
	XMC_SV64   = 17 // 64位主管调用描述符
	XMC_SV3264 = 18 // 32位和64位的主管调用描述符
	XMC_TL     = 20 // 读/写线程本地数据
	XMC_UL     = 21 // 读/写线程本地数据（.tbss）
	XMC_TE     = 22 // TOC条目
)

// 装载机收割台
type XcoffLdHdr64 struct {
	Lversion int32  // 装载机部分版本号
	Lnsyms   int32  // 符号表条目数
	Lnreloc  int32  // 重定位表项的数目
	Listlen  uint32 // 导入文件ID字符串表的长度
	Lnimpid  int32  // 导入文件ID的数量
	Lstlen   uint32 // 字符串表的长度
	Limpoff  uint64 // 导入文件ID开始的偏移量
	Lstoff   uint64 // 到字符串表开头的偏移量
	Lsymoff  uint64 // 到符号表起点的偏移量
	Lrldoff  uint64 // 重新定位条目的起始偏移量
}

// 加载器符号
type XcoffLdSym64 struct {
	Lvalue  uint64 // 地址字段
	Loffset uint32 // 字节偏移量到符号名称的字符串表中
	Lscnum  int16  // 包含符号的节号
	Lsmtype int8   // 符号类型、导出、导入标志
	Lsmclas int8   // 符号存储类
	Lifile  int32  // 导入文件ID；导入文件ID的序号
	Lparm   uint32 // 参数类型检查字段
}

type xcoffLoaderSymbol struct {
	sym    loader.Sym
	smtype int8
	smclas int8
}

type XcoffLdImportFile64 struct {
	Limpidpath string
	Limpidbase string
	Limpidmem  string
}

type XcoffLdRel64 struct {
	Lvaddr  uint64 // 地址字段
	Lrtype  uint16 // 搬迁规模和类型
	Lrsecnm int16  // 正在搬迁的路段编号
	Lsymndx int32  // 加载器段符号表索引
}

// XCDowloaderreloc保存有关加载程序重新定位的信息。
type xcoffLoaderReloc struct {
	sym    loader.Sym
	roff   int32
	rtype  uint16
	symndx int32
}

const (
	XCOFF_R_POS = 0x00 // A（sym）正迁移
	XCOFF_R_NEG = 0x01 // -A（sym）负迁移
	XCOFF_R_REL = 0x02 // 相对于自身的（sym-*）
	XCOFF_R_TOC = 0x03 // A（sym TOC）相对于TOC
	XCOFF_R_TRL = 0x12 // A（sym TOC）TOC相对间接负荷。

	XCOFF_R_TRLA = 0x13 // （sym TOC）TOC Rel加载地址。可修改仪表
	XCOFF_R_GL   = 0x05 // A（sym的外部TOC）全球链接
	XCOFF_R_TCL  = 0x06 // A（sym的本地TOC）本地对象TOC地址
	XCOFF_R_RL   = 0x0C // A（sym）位置间接负载。可修改指令
	XCOFF_R_RLA  = 0x0D // 一个（sym）Pos加载地址。可修改指令
	XCOFF_R_REF  = 0x0F // AL0（sym）非重新定位参考无垃圾收集
	XCOFF_R_BA   = 0x08 // （符号）绝对分支。无法修改指令
	XCOFF_R_RBA  = 0x18 // （符号）绝对分支。可修改指令
	XCOFF_R_BR   = 0x0A // 与自身相关的（sym-*）分支。不可修改
	XCOFF_R_RBR  = 0x1A // 与自身相关的（sym-*）分支。可修改仪表

	XCOFF_R_TLS    = 0x20 // TLS符号的通用动态参考
	XCOFF_R_TLS_IE = 0x21 // TLS符号的初始exec引用
	XCOFF_R_TLS_LD = 0x22 // TLS符号的局部动态引用
	XCOFF_R_TLS_LE = 0x23 // 对TLS符号的本地exec引用
	XCOFF_R_TLSM   = 0x24 // TLS符号的模块参考
	XCOFF_R_TLSML  = 0x25 // 本地（自身）模块的模块引用

	XCOFF_R_TOCU = 0x30 // 相对于TOC-高阶位
	XCOFF_R_TOCL = 0x31 // 相对于TOC-低阶位
)

type XcoffLdStr64 struct {
	size uint16
	name string
}

// XCOFF文件用于构建XCOFF文件。
type xcoffFile struct {
	xfhdr           XcoffFileHdr64
	xahdr           XcoffAoutHdr64
	sections        []*XcoffScnHdr64
	sectText        *XcoffScnHdr64
	sectData        *XcoffScnHdr64
	sectBss         *XcoffScnHdr64
	stringTable     xcoffStringTable
	sectNameToScnum map[string]int16
	loaderSize      uint64
	symtabOffset    int64                // 到符号表起点的偏移量
	symbolCount     uint32               // 写入的符号表记录数
	symtabSym       []xcoffSym           // XCOFF符号表的符号
	dynLibraries    map[string]int       // .loader部分中的动态库。整数表示其导入文件编号（-1）
	loaderSymbols   []*xcoffLoaderSymbol // 加载程序符号表中的符号
	loaderReloc     []*xcoffLoaderReloc  // 必须在加载器内部进行的重新加载
	sync.Mutex                           // 当前保护loaderReloc
}

// XCOFF生成算法使用的Var
var (
	xfile xcoffFile
)

// XCOFSTRINGTABLE是一个XCOFF字符串表。
type xcoffStringTable struct {
	strings    []string
	stringsLen int
}

// size返回字符串表t的大小。
func (t *xcoffStringTable) size() int {
	// 字符串表以4字节长度开头
	return t.stringsLen + 4
}

// add将字符串str添加到字符串表t。
func (t *xcoffStringTable) add(str string) int {
	off := t.size()
	t.strings = append(t.strings, str)
	t.stringsLen += len(str) + 1 // 每个字符串将附加0
	return off
}

// write将字符串表t写入输出文件。
func (t *xcoffStringTable) write(out *OutBuf) {
	out.Write32(uint32(t.size()))
	for _, s := range t.strings {
		out.WriteString(s)
		out.Write8(0)
	}
}

// 将XCOFF节写入输出文件。
func (sect *XcoffScnHdr64) write(ctxt *Link) {
	binary.Write(ctxt.Out, binary.BigEndian, sect)
	ctxt.Out.Write32(0) // 在末尾添加4个空字节以匹配对齐方式
}

// addSection将节添加到XCOFF文件f。
func (f *xcoffFile) addSection(name string, addr uint64, size uint64, fileoff uint64, flags uint32) *XcoffScnHdr64 {
	sect := &XcoffScnHdr64{
		Spaddr:  addr,
		Svaddr:  addr,
		Ssize:   size,
		Sscnptr: fileoff,
		Sflags:  flags,
	}
	copy(sect.Sname[:], name) // 将字符串复制到[8]字节
	f.sections = append(f.sections, sect)
	f.sectNameToScnum[name] = int16(len(f.sections))
	return sect
}

// addDwarfSection将一个dwarf节添加到XCOFF文件f中。
// 此函数类似于addSection，但节名较短
// 必须修改为常规名称，并且它们是不同的子类型。
func (f *xcoffFile) addDwarfSection(s *sym.Section) *XcoffScnHdr64 {
	newName, subtype := xcoffGetDwarfSubtype(s.Name)
	return f.addSection(newName, 0, s.Length, s.Seg.Fileoff+s.Vaddr-s.Seg.Vaddr, STYP_DWARF|subtype)
}

// xcoffGetDwarfSubtype返回矮段str的XCOFF名称
// 及其子类型常数。
func xcoffGetDwarfSubtype(str string) (string, uint32) {
	switch str {
	default:
		Exitf("unknown DWARF section name for XCOFF: %s", str)
	case ".debug_abbrev":
		return ".dwabrev", SSUBTYP_DWABREV
	case ".debug_info":
		return ".dwinfo", SSUBTYP_DWINFO
	case ".debug_frame":
		return ".dwframe", SSUBTYP_DWFRAME
	case ".debug_line":
		return ".dwline", SSUBTYP_DWLINE
	case ".debug_loc":
		return ".dwloc", SSUBTYP_DWLOC
	case ".debug_pubnames":
		return ".dwpbnms", SSUBTYP_DWPBNMS
	case ".debug_pubtypes":
		return ".dwpbtyp", SSUBTYP_DWPBTYP
	case ".debug_ranges":
		return ".dwrnges", SSUBTYP_DWRNGES
	}
	// 从未使用过
	return "", 0
}

// GetXCoffCnum返回Go区段的XCOFF区段编号。
func (f *xcoffFile) getXCOFFscnum(sect *sym.Section) int16 {
	switch sect.Seg {
	case &Segtext:
		return f.sectNameToScnum[".text"]
	case &Segdata:
		if sect.Name == ".noptrbss" || sect.Name == ".bss" {
			return f.sectNameToScnum[".bss"]
		}
		if sect.Name == ".tbss" {
			return f.sectNameToScnum[".tbss"]
		}
		return f.sectNameToScnum[".data"]
	case &Segdwarf:
		name, _ := xcoffGetDwarfSubtype(sect.Name)
		return f.sectNameToScnum[name]
	case &Segrelrodata:
		return f.sectNameToScnum[".data"]
	}
	Errorf(nil, "getXCOFFscnum not implemented for section %s", sect.Name)
	return -1
}

// Xcoffinit初始化了一些内部值和设置
// 已知的头信息
func Xcoffinit(ctxt *Link) {
	xfile.dynLibraries = make(map[string]int)

	HEADR = int32(Rnd(XCOFFHDRRESERVE, XCOFFSECTALIGN))
	if *FlagTextAddr != -1 {
		Errorf(nil, "-T not available on AIX")
	}
	*FlagTextAddr = XCOFFTEXTBASE + int64(HEADR)
	if *FlagRound != -1 {
		Errorf(nil, "-R not available on AIX")
	}
	*FlagRound = int(XCOFFSECTALIGN)

}

// 符号表

// 在XCOFF中键入genasmsym所需的C_文件信息。
type xcoffSymSrcFile struct {
	name         string
	file         *XcoffSymEnt64   // 此C_文件的符号
	csectAux     *XcoffAuxCSect64 // 当前.csect的符号
	csectSymNb   uint64           // 当前.csect的符号编号
	csectVAStart int64
	csectVAEnd   int64
}

var (
	currDwscnoff   = make(map[string]uint64) // 需要创建C_矮人符号
	currSymSrcFile xcoffSymSrcFile
	outerSymSize   = make(map[string]int64)
)

// xcoffUpdateOuterSize存储外部符号的大小，以便
// 在符号表中。
func xcoffUpdateOuterSize(ctxt *Link, size int64, stype sym.SymKind) {
	if size == 0 {
		return
	}
	// TODO:使用CarrierSymByType

	ldr := ctxt.loader
	switch stype {
	default:
		Errorf(nil, "unknown XCOFF outer symbol for type %s", stype.String())
	case sym.SRODATA, sym.SRODATARELRO, sym.SFUNCTAB, sym.SSTRING:
		// 无事可做
	case sym.STYPERELRO:
		if ctxt.UseRelro() && (ctxt.BuildMode == BuildModeCArchive || ctxt.BuildMode == BuildModeCShared || ctxt.BuildMode == BuildModePIE) {
			// 必须删除runtime.types大小，因为它是真实符号。
			tsize := ldr.SymSize(ldr.Lookup("runtime.types", 0))
			outerSymSize["typerel.*"] = size - tsize
			return
		}
		fallthrough
	case sym.STYPE:
		if !ctxt.DynlinkingGo() {
			// 必须删除runtime.types大小，因为它是真实符号。
			tsize := ldr.SymSize(ldr.Lookup("runtime.types", 0))
			outerSymSize["type.*"] = size - tsize
		}
	case sym.SGOSTRING:
		outerSymSize["go.string.*"] = size
	case sym.SGOFUNC:
		if !ctxt.DynlinkingGo() {
			outerSymSize["go.func.*"] = size
		}
	case sym.SGOFUNCRELRO:
		outerSymSize["go.funcrel.*"] = size
	case sym.SGCBITS:
		outerSymSize["runtime.gcbits.*"] = size
	case sym.SPCLNTAB:
		outerSymSize["runtime.pclntab"] = size
	}
}

// addSymbol在ctxt.out上写入符号或辅助符号条目。
func (f *xcoffFile) addSymbol(sym xcoffSym) {
	f.symtabSym = append(f.symtabSym, sym)
	f.symbolCount++
}

// xcoffAlign返回符号对齐的对数基数2。
func xcoffAlign(ldr *loader.Loader, x loader.Sym, t SymbolType) uint8 {
	align := ldr.SymAlign(x)
	if align == 0 {
		if t == TextSym {
			align = int32(Funcalign)
		} else {
			align = symalign(ldr, x)
		}
	}
	return logBase2(int(align))
}

// logBase2返回a的以2为底的日志。
func logBase2(a int) uint8 {
	return uint8(bits.Len(uint(a)) - 1)
}

// 新文件出现时需要写入符号：
// -一个C_文件，其名称有一个辅助条目
// -C_矮人符号提供调试信息
// -一个C_隐藏文本，它将是一个包含其所有函数的csect
// 它需要几个参数来创建.csect符号，例如其入口点和节号。
// None
// 目前，一个新文件实际上是一个新的包。看起来还可以，但可能会改变
// 未来
func (f *xcoffFile) writeSymbolNewFile(ctxt *Link, name string, firstEntry uint64, extnum int16) {
	ldr := ctxt.loader
	/* C_FILE */
	s := &XcoffSymEnt64{
		Noffset: uint32(f.stringTable.add(".file")),
		Nsclass: C_FILE,
		Nscnum:  N_DEBUG,
		Ntype:   0, // Go不在预定义语言中。
		Nnumaux: 1,
	}
	f.addSymbol(s)
	currSymSrcFile.file = s

	// 文件名的辅助项。
	auxf := &XcoffAuxFile64{
		Xoffset:  uint32(f.stringTable.add(name)),
		Xftype:   XFT_FN,
		Xauxtype: _AUX_FILE,
	}
	f.addSymbol(auxf)

	/* Dwarf */
	for _, sect := range Segdwarf.Sections {
		var dwsize uint64
		if ctxt.LinkMode == LinkInternal {
			// 查找此相应包矮人编译单元的大小。
			// 此大小在矮人生成期间设置（请参见DWARF.go）。
			dwsize = getDwsectCUSize(sect.Name, name)
			// .debug_abbrev对于所有包都是通用的，在前面的函数中找不到
			if sect.Name == ".debug_abbrev" {
				dwsize = uint64(ldr.SymSize(loader.Sym(sect.Sym)))

			}
		} else {
			// 只有一个带有外部链接的.FILE。
			dwsize = sect.Length
		}

		// 获取XCOFF名称
		name, _ := xcoffGetDwarfSubtype(sect.Name)
		s := &XcoffSymEnt64{
			Nvalue:  currDwscnoff[sect.Name],
			Noffset: uint32(f.stringTable.add(name)),
			Nsclass: C_DWARF,
			Nscnum:  f.getXCOFFscnum(sect),
			Nnumaux: 1,
		}

		if currSymSrcFile.csectAux == nil {
			// Dwarf重定位需要.dw*符号的符号数。
			// 它不需要知道每一个包，一个就足够了。
			// currSymSrcFile.csectAux==nil表示第一个包。
			ldr.SetSymDynid(loader.Sym(sect.Sym), int32(f.symbolCount))

			if sect.Name == ".debug_frame" && ctxt.LinkMode != LinkExternal {
				// CIE尺寸必须添加到第一个包装中。
				dwsize += 48
			}
		}

		f.addSymbol(s)

		// 更新此文件中的DWARF截面偏移量
		if sect.Name != ".debug_abbrev" {
			currDwscnoff[sect.Name] += dwsize
		}

		// 辅助矮段
		auxd := &XcoffAuxDWARF64{
			Xscnlen:  dwsize,
			Xauxtype: _AUX_SECT,
		}

		f.addSymbol(auxd)
	}

	/* .csect */
	// 检查extnum是否在文本中。
	// 这是暂时的，仅用于检查此算法是否正确。
	if extnum != 1 {
		Exitf("XCOFF symtab: A new file was detected with its first symbol not in .text")
	}

	currSymSrcFile.csectSymNb = uint64(f.symbolCount)

	// 没有偏移，因为没有名称
	s = &XcoffSymEnt64{
		Nvalue:  firstEntry,
		Nscnum:  extnum,
		Nsclass: C_HIDEXT,
		Ntype:   0, // 检查能见度？
		Nnumaux: 1,
	}
	f.addSymbol(s)

	aux := &XcoffAuxCSect64{
		Xsmclas:  XMC_PR,
		Xsmtyp:   XTY_SD | logBase2(Funcalign)<<3,
		Xauxtype: _AUX_CSECT,
	}
	f.addSymbol(aux)

	currSymSrcFile.csectAux = aux
	currSymSrcFile.csectVAStart = int64(firstEntry)
	currSymSrcFile.csectVAEnd = int64(firstEntry)
}

// 更新上一个包的值。
// -C_文件符号的S值：如果是最后一个，则该S值必须为-1
// -csect符号的Xsclen。
func (f *xcoffFile) updatePreviousFile(ctxt *Link, last bool) {
	// 第一档
	if currSymSrcFile.file == nil {
		return
	}

	// 更新C_文件
	cfile := currSymSrcFile.file
	if last {
		cfile.Nvalue = 0xFFFFFFFFFFFFFFFF
	} else {
		cfile.Nvalue = uint64(f.symbolCount)
	}

	// 在此辅助条目中更新csect scnlen
	aux := currSymSrcFile.csectAux
	csectSize := currSymSrcFile.csectVAEnd - currSymSrcFile.csectVAStart
	aux.Xscnlenlo = uint32(csectSize & 0xFFFFFFFF)
	aux.Xscnlenhi = uint32(csectSize >> 32)
}

// 写入表示.text函数的符号。
// 符号表用对应于每个包的C_文件分割
// 而不是像它应该的那样发送到每个源文件。
func (f *xcoffFile) writeSymbolFunc(ctxt *Link, x loader.Sym) []xcoffSym {
	// 将写入的新XCOFF符号。
	syms := []xcoffSym{}

	// 检查是否检测到新文件。
	ldr := ctxt.loader
	name := ldr.SymName(x)
	if strings.Contains(name, "-tramp") || strings.HasPrefix(name, "runtime.text.") {
		// 蹦床没有锉刀，所以有考虑
		// 在当前文件中。
		// runtime.text.X符号也是如此。
	} else if ldr.SymPkg(x) == "" { // 未定义的全局符号
		// 如果发生这种情况，则必须重做算法。
		if currSymSrcFile.name != "" {
			Exitf("undefined global symbol found inside another file")
		}
	} else {
		// 当前文件已更改。必须生成新的C_文件、C_DWARF等。
		if currSymSrcFile.name != ldr.SymPkg(x) {
			if ctxt.LinkMode == LinkInternal {
				// 更新以前的文件值
				xfile.updatePreviousFile(ctxt, false)
				currSymSrcFile.name = ldr.SymPkg(x)
				f.writeSymbolNewFile(ctxt, ldr.SymPkg(x), uint64(ldr.SymValue(x)), xfile.getXCOFFscnum(ldr.SymSect(x)))
			} else {
				// 使用外部链接时，如果存在多个链接，ld将崩溃
				// .FILE和DWARF调试在调试过程中的某个位置启用
				// 搬迁阶段。
				// 因此，所有包都合并到一个伪.FILE下
				// “go_函数”。
				// TODO（aix）；一旦ld被固定或触发，移除
				// 已经找到并修复了重新安置。
				if currSymSrcFile.name == "" {
					currSymSrcFile.name = ldr.SymPkg(x)
					f.writeSymbolNewFile(ctxt, "go_functions", uint64(ldr.SymValue(x)), xfile.getXCOFFscnum(ldr.SymSect(x)))
				}
			}

		}
	}

	s := &XcoffSymEnt64{
		Nsclass: C_EXT,
		Noffset: uint32(xfile.stringTable.add(ldr.SymExtname(x))),
		Nvalue:  uint64(ldr.SymValue(x)),
		Nscnum:  f.getXCOFFscnum(ldr.SymSect(x)),
		Ntype:   SYM_TYPE_FUNC,
		Nnumaux: 2,
	}

	if ldr.IsFileLocal(x) || ldr.AttrVisibilityHidden(x) || ldr.AttrLocal(x) {
		s.Nsclass = C_HIDEXT
	}

	ldr.SetSymDynid(x, int32(xfile.symbolCount))
	syms = append(syms, s)

	// 通过跟踪VA范围跟踪截面尺寸。个人
	// 对齐差异可能会引入一些额外的填充字节
	// 未被ldr.SymSize（x）完全解释。
	sv := ldr.SymValue(x) + ldr.SymSize(x)
	if currSymSrcFile.csectVAEnd < sv {
		currSymSrcFile.csectVAEnd = sv
	}

	// 创建辅助条目
	a2 := &XcoffAuxFcn64{
		Xfsize:   uint32(ldr.SymSize(x)),
		Xlnnoptr: 0,                     // 待办事项
		Xendndx:  xfile.symbolCount + 3, // 此符号+2个辅助条目
		Xauxtype: _AUX_FCN,
	}
	syms = append(syms, a2)

	a4 := &XcoffAuxCSect64{
		Xscnlenlo: uint32(currSymSrcFile.csectSymNb & 0xFFFFFFFF),
		Xscnlenhi: uint32(currSymSrcFile.csectSymNb >> 32),
		Xsmclas:   XMC_PR, // 程序代码
		Xsmtyp:    XTY_LD, // 标签定义（基于C）
		Xauxtype:  _AUX_CSECT,
	}
	a4.Xsmtyp |= uint8(xcoffAlign(ldr, x, TextSym) << 3)

	syms = append(syms, a4)
	return syms
}

// genasmsym用于写入符号表的put函数
func putaixsym(ctxt *Link, x loader.Sym, t SymbolType) {
	// 此GO符号生成的所有XCOFF符号
	// 可以是符号条目或辅助条目
	syms := []xcoffSym{}

	ldr := ctxt.loader
	name := ldr.SymName(x)
	if t == UndefinedSym {
		name = ldr.SymExtname(x)
	}

	switch t {
	default:
		return

	case TextSym:
		if ldr.SymPkg(x) != "" || strings.Contains(name, "-tramp") || strings.HasPrefix(name, "runtime.text.") {
			// 文件中的函数
			syms = xfile.writeSymbolFunc(ctxt, x)
		} else {
			// 只有runtime.text和runtime.etext通过这种方式
			if name != "runtime.text" && name != "runtime.etext" && name != "go.buildid" {
				Exitf("putaixsym: unknown text symbol %s", name)
			}
			s := &XcoffSymEnt64{
				Nsclass: C_HIDEXT,
				Noffset: uint32(xfile.stringTable.add(name)),
				Nvalue:  uint64(ldr.SymValue(x)),
				Nscnum:  xfile.getXCOFFscnum(ldr.SymSect(x)),
				Ntype:   SYM_TYPE_FUNC,
				Nnumaux: 1,
			}
			ldr.SetSymDynid(x, int32(xfile.symbolCount))
			syms = append(syms, s)

			size := uint64(ldr.SymSize(x))
			a4 := &XcoffAuxCSect64{
				Xauxtype:  _AUX_CSECT,
				Xscnlenlo: uint32(size & 0xFFFFFFFF),
				Xscnlenhi: uint32(size >> 32),
				Xsmclas:   XMC_PR,
				Xsmtyp:    XTY_SD,
			}
			a4.Xsmtyp |= uint8(xcoffAlign(ldr, x, TextSym) << 3)
			syms = append(syms, a4)
		}

	case DataSym, BSSSym:
		s := &XcoffSymEnt64{
			Nsclass: C_EXT,
			Noffset: uint32(xfile.stringTable.add(name)),
			Nvalue:  uint64(ldr.SymValue(x)),
			Nscnum:  xfile.getXCOFFscnum(ldr.SymSect(x)),
			Nnumaux: 1,
		}

		if ldr.IsFileLocal(x) || ldr.AttrVisibilityHidden(x) || ldr.AttrLocal(x) {
			// 在全局数据的情况下，有更多的符号
			// 与生成的程序集相关的
			// 访问这些符号。
			// 但作为Golang自己的方式来检查符号是否正确
			// 全球或本地（大写字母），我们不需要
			// 还没有实施。
			s.Nsclass = C_HIDEXT
		}

		ldr.SetSymDynid(x, int32(xfile.symbolCount))
		syms = append(syms, s)

		// 创建辅助条目

		// 通常，大小应为包含所有内容的csect的大小
		// 一个文件/包的数据和bss符号。
		// 但是，只为每个符号指定一个csect更容易。
		// 它可能会改变
		size := uint64(ldr.SymSize(x))
		a4 := &XcoffAuxCSect64{
			Xauxtype:  _AUX_CSECT,
			Xscnlenlo: uint32(size & 0xFFFFFFFF),
			Xscnlenhi: uint32(size >> 32),
		}

		if ty := ldr.SymType(x); ty >= sym.STYPE && ty <= sym.SPCLNTAB {
			if ctxt.IsExternal() && strings.HasPrefix(ldr.SymSect(x).Name, ".data.rel.ro") {
				// 在外部链接期间，只读数据会重新定位
				// 必须在。数据中。
				a4.Xsmclas = XMC_RW
			} else {
				// 只读数据
				a4.Xsmclas = XMC_RO
			}
		} else if /*ty == sym.SDATA &&*/ strings.HasPrefix(ldr.SymName(x), "TOC.") && ctxt.IsExternal() {
			a4.Xsmclas = XMC_TC
		} else if ldr.SymName(x) == "TOC" {
			a4.Xsmclas = XMC_TC0
		} else {
			a4.Xsmclas = XMC_RW
		}
		if t == DataSym {
			a4.Xsmtyp |= XTY_SD
		} else {
			a4.Xsmtyp |= XTY_CM
		}

		a4.Xsmtyp |= uint8(xcoffAlign(ldr, x, t) << 3)

		syms = append(syms, a4)

	case UndefinedSym:
		if ty := ldr.SymType(x); ty != sym.SDYNIMPORT && ty != sym.SHOSTOBJ && ty != sym.SUNDEFEXT {
			return
		}
		s := &XcoffSymEnt64{
			Nsclass: C_EXT,
			Noffset: uint32(xfile.stringTable.add(name)),
			Nnumaux: 1,
		}
		ldr.SetSymDynid(x, int32(xfile.symbolCount))
		syms = append(syms, s)

		a4 := &XcoffAuxCSect64{
			Xauxtype: _AUX_CSECT,
			Xsmclas:  XMC_DS,
			Xsmtyp:   XTY_ER | XTY_IMP,
		}

		if ldr.SymName(x) == "__n_pthreads" {
			// 目前，cgo_import_制作的所有导入符号都是动态的
			// 系统调用函数，但作为变量的_n_pthreads除外。
			// TODO（aix）：找到一种检测cgo导入的变量的方法。
			a4.Xsmclas = XMC_RW
		}

		syms = append(syms, a4)

	case TLSSym:
		s := &XcoffSymEnt64{
			Nsclass: C_EXT,
			Noffset: uint32(xfile.stringTable.add(name)),
			Nscnum:  xfile.getXCOFFscnum(ldr.SymSect(x)),
			Nvalue:  uint64(ldr.SymValue(x)),
			Nnumaux: 1,
		}

		ldr.SetSymDynid(x, int32(xfile.symbolCount))
		syms = append(syms, s)

		size := uint64(ldr.SymSize(x))
		a4 := &XcoffAuxCSect64{
			Xauxtype:  _AUX_CSECT,
			Xsmclas:   XMC_UL,
			Xsmtyp:    XTY_CM,
			Xscnlenlo: uint32(size & 0xFFFFFFFF),
			Xscnlenhi: uint32(size >> 32),
		}

		syms = append(syms, a4)
	}

	for _, s := range syms {
		xfile.addSymbol(s)
	}
}

// 生成XCOFF符号表。
// 它将在Asmbxcoff中写入输出文件，因为它必须
// 最后，特别是需要符号索引的重新定位部分。
func (f *xcoffFile) asmaixsym(ctxt *Link) {
	ldr := ctxt.loader
	// 获取包装其他符号（如go.string）的符号的正确大小*
	// 符号大小可以直接使用，因为符号已经写入。
	for name, size := range outerSymSize {
		sym := ldr.Lookup(name, 0)
		if sym == 0 {
			Errorf(nil, "unknown outer symbol with name %s", name)
		} else {
			s := ldr.MakeSymbolUpdater(sym)
			s.SetSize(size)
		}
	}

	// 这些符号不会出现在下面的第一个循环中，因为我们
	// 跳过sym.STEXT符号。正常sym.STEXT符号由行走textp发出。
	s := ldr.Lookup("runtime.text", 0)
	if ldr.SymType(s) == sym.STEXT {
		// 我们已经在带有外部链接器的AIX上的ctxt.Textp中包含了这个符号。
		// 请参阅data.go:/text地址
		if !ctxt.IsExternal() {
			putaixsym(ctxt, s, TextSym)
		}
	}

	n := 1
	// 如果有多个文本节，则为所有文本节生成基址
	for _, sect := range Segtext.Sections[1:] {
		if sect.Name != ".text" || ctxt.IsExternal() {
			// 在AIX上，runtime.text.X是符号选项卡中已有的符号。
			break
		}
		s = ldr.Lookup(fmt.Sprintf("runtime.text.%d", n), 0)
		if s == 0 {
			break
		}
		if ldr.SymType(s) == sym.STEXT {
			putaixsym(ctxt, s, TextSym)
		}
		n++
	}

	s = ldr.Lookup("runtime.etext", 0)
	if ldr.SymType(s) == sym.STEXT {
		// 我们已经在ctxt.Textp中包含了这个符号
		// 在带有外部链接器的AIX上。
		// 请参阅data.go:/text地址
		if !ctxt.IsExternal() {
			putaixsym(ctxt, s, TextSym)
		}
	}

	shouldBeInSymbolTable := func(s loader.Sym, name string) bool {
		if name == ".go.buildinfo" {
			// 在AIX上，.go.buildinfo必须作为
			// 它有搬迁。
			return true
		}
		if ldr.AttrNotInSymbolTable(s) {
			return false
		}
		if (name == "" || name[0] == '.') && !ldr.IsFileLocal(s) && name != ".TOC." {
			return false
		}
		return true
	}

	for s, nsym := loader.Sym(1), loader.Sym(ldr.NSym()); s < nsym; s++ {
		if !shouldBeInSymbolTable(s, ldr.SymName(s)) {
			continue
		}
		st := ldr.SymType(s)
		switch {
		case st == sym.STLSBSS:
			if ctxt.IsExternal() {
				putaixsym(ctxt, s, TLSSym)
			}

		case st == sym.SBSS, st == sym.SNOPTRBSS, st == sym.SLIBFUZZER_EXTRA_COUNTER:
			if ldr.AttrReachable(s) {
				data := ldr.Data(s)
				if len(data) > 0 {
					ldr.Errorf(s, "should not be bss (size=%d type=%v special=%v)", len(data), ldr.SymType(s), ldr.AttrSpecial(s))
				}
				putaixsym(ctxt, s, BSSSym)
			}

		case st >= sym.SELFRXSECT && st < sym.SXREF: // 用dodata处理的数据段
			if ldr.AttrReachable(s) {
				putaixsym(ctxt, s, DataSym)
			}

		case st == sym.SUNDEFEXT:
			putaixsym(ctxt, s, UndefinedSym)

		case st == sym.SDYNIMPORT:
			if ldr.AttrReachable(s) {
				putaixsym(ctxt, s, UndefinedSym)
			}
		}
	}

	for _, s := range ctxt.Textp {
		putaixsym(ctxt, s, TextSym)
	}

	if ctxt.Debugvlog != 0 || *flagN {
		ctxt.Logf("symsize = %d\n", uint32(symSize))
	}
	xfile.updatePreviousFile(ctxt, true)
}

func (f *xcoffFile) genDynSym(ctxt *Link) {
	ldr := ctxt.loader
	var dynsyms []loader.Sym
	for s := loader.Sym(1); s < loader.Sym(ldr.NSym()); s++ {
		if !ldr.AttrReachable(s) {
			continue
		}
		if t := ldr.SymType(s); t != sym.SHOSTOBJ && t != sym.SDYNIMPORT {
			continue
		}
		dynsyms = append(dynsyms, s)
	}

	for _, s := range dynsyms {
		f.adddynimpsym(ctxt, s)

		if _, ok := f.dynLibraries[ldr.SymDynimplib(s)]; !ok {
			f.dynLibraries[ldr.SymDynimplib(s)] = len(f.dynLibraries)
		}
	}
}

// （*xcoffile）adddynimpsym将动态符号“s”添加到XCOFF文件中。
// 将创建一个名为s.Extname（）的新符号作为实际的动态符号
// 在.loader部分和符号表中作为外部引用。
// 符号“s”被转换为SXCOFFTOC，最终位于.data部分。
// 但是，这些符号和符号上没有文字保护
// 可能需要添加它。
// TODO（aix）：处理不带库的动态符号。
func (f *xcoffFile) adddynimpsym(ctxt *Link, s loader.Sym) {
	// 检查是否提供了库名称。
	// 编译时已检查模式。
	ldr := ctxt.loader
	if ctxt.IsInternal() && ldr.SymDynimplib(s) == "" {
		ctxt.Errorf(s, "imported symbol must have a given library")
	}

	sb := ldr.MakeSymbolUpdater(s)
	sb.SetReachable(true)
	sb.SetType(sym.SXCOFFTOC)

	// 创建新的动态符号
	extsym := ldr.CreateSymForUpdate(ldr.SymExtname(s), 0)
	extsym.SetType(sym.SDYNIMPORT)
	extsym.SetDynimplib(ldr.SymDynimplib(s))
	extsym.SetExtname(ldr.SymExtname(s))
	extsym.SetDynimpvers(ldr.SymDynimpvers(s))

	// 添加加载器符号
	lds := &xcoffLoaderSymbol{
		sym:    extsym.Sym(),
		smtype: XTY_IMP,
		smclas: XMC_DS,
	}
	if ldr.SymName(s) == "__n_pthreads" {
		// 目前，cgo_import_制作的所有导入符号都是动态的
		// 系统调用函数，但作为变量的_n_pthreads除外。
		// TODO（aix）：找到一种检测cgo导入的变量的方法。
		lds.smclas = XMC_RW
	}
	f.loaderSymbols = append(f.loaderSymbols, lds)

	// 重新定位以检索外部地址
	sb.AddBytes(make([]byte, 8))
	r, _ := sb.AddRel(objabi.R_ADDR)
	r.SetSym(extsym.Sym())
	r.SetSiz(uint8(ctxt.Arch.PtrSize))
	// 托多：也许这可能是
	// 某人设置大小（0）
	// sb.设置数据（无）
	// sb.addr（ctxt.Arch，extsym.Sym（））
	// 如果开始时大小不是0，我认为不会添加8个字节
	// 零的数量是必需的。
}

// XCOFFADDYNREL在XCOFF文件中添加动态重定位。
// 此重新定位将由装载机进行。
func Xcoffadddynrel(target *Target, ldr *loader.Loader, syms *ArchSyms, s loader.Sym, r loader.Reloc, rIdx int) bool {
	if target.IsExternal() {
		return true
	}
	if ldr.SymType(s) <= sym.SPCLNTAB {
		ldr.Errorf(s, "cannot have a relocation to %s in a text section symbol", ldr.SymName(r.Sym()))
		return false
	}

	xldr := &xcoffLoaderReloc{
		sym:  s,
		roff: r.Off(),
	}
	targ := ldr.ResolveABIAlias(r.Sym())
	var targType sym.SymKind
	if targ != 0 {
		targType = ldr.SymType(targ)
	}

	switch r.Type() {
	default:
		ldr.Errorf(s, "unexpected .loader relocation to symbol: %s (type: %s)", ldr.SymName(targ), r.Type().String())
		return false
	case objabi.R_ADDR:
		if ldr.SymType(s) == sym.SXCOFFTOC && targType == sym.SDYNIMPORT {
			// 导入的符号重新定位
			for i, dynsym := range xfile.loaderSymbols {
				if ldr.SymName(dynsym.sym) == ldr.SymName(targ) {
					xldr.symndx = int32(i + 3) // +3因为有3个剖面符号
					break
				}
			}
		} else if t := ldr.SymType(s); t == sym.SDATA || t == sym.SNOPTRDATA || t == sym.SBUILDINFO || t == sym.SXCOFFTOC {
			switch ldr.SymSect(targ).Seg {
			default:
				ldr.Errorf(s, "unknown segment for .loader relocation with symbol %s", ldr.SymName(targ))
			case &Segtext:
			case &Segrodata:
				xldr.symndx = 0 // 文本
			case &Segdata:
				if targType == sym.SBSS || targType == sym.SNOPTRBSS {
					xldr.symndx = 2 // .bss
				} else {
					xldr.symndx = 1 // 数据
				}
			}

		} else {
			ldr.Errorf(s, "unexpected type for .loader relocation R_ADDR for symbol %s: %s to %s", ldr.SymName(targ), ldr.SymType(s), ldr.SymType(targ))
			return false
		}

		xldr.rtype = 0x3F<<8 + XCOFF_R_POS
	}

	xfile.Lock()
	xfile.loaderReloc = append(xfile.loaderReloc, xldr)
	xfile.Unlock()
	return true
}

func (ctxt *Link) doxcoff() {
	if *FlagD {
		// 由于系统调用，所有XCOFF文件都有动态符号。
		Exitf("-d is not available on AIX")
	}
	ldr := ctxt.loader

	// TOC
	toc := ldr.CreateSymForUpdate("TOC", 0)
	toc.SetType(sym.SXCOFFTOC)
	toc.SetVisibilityHidden(true)

	// 将入口点添加到.loader符号。
	ep := ldr.Lookup(*flagEntrySymbol, 0)
	if ep == 0 || !ldr.AttrReachable(ep) {
		Exitf("wrong entry point")
	}

	xfile.loaderSymbols = append(xfile.loaderSymbols, &xcoffLoaderSymbol{
		sym:    ep,
		smtype: XTY_ENT | XTY_SD,
		smclas: XMC_DS,
	})

	xfile.genDynSym(ctxt)

	for s := loader.Sym(1); s < loader.Sym(ldr.NSym()); s++ {
		if strings.HasPrefix(ldr.SymName(s), "TOC.") {
			sb := ldr.MakeSymbolUpdater(s)
			sb.SetType(sym.SXCOFFTOC)
		}
	}

	if ctxt.IsExternal() {
		// 在运行时/cgo:main（）中将rt0\U go名称更改为与名称匹配。
		rt0 := ldr.Lookup("runtime.rt0_go", 0)
		ldr.SetSymExtname(rt0, "runtime_rt0_go")

		nsym := loader.Sym(ldr.NSym())
		for s := loader.Sym(1); s < nsym; s++ {
			if !ldr.AttrCgoExport(s) {
				continue
			}
			if ldr.IsFileLocal(s) {
				panic("cgo_export on static symbol")
			}

			if ldr.SymType(s) == sym.STEXT || ldr.SymType(s) == sym.SABIALIAS {
				// 在AIX上，导出的函数必须有两个符号：
				// -必须以“.”开头的文本符号。
				// -数据符号，它是一个函数描述符。
				name := ldr.SymExtname(s)
				ldr.SetSymExtname(s, "."+name)

				desc := ldr.MakeSymbolUpdater(ldr.CreateExtSym(name, 0))
				desc.SetReachable(true)
				desc.SetType(sym.SNOPTRDATA)
				desc.AddAddr(ctxt.Arch, s)
				desc.AddAddr(ctxt.Arch, toc.Sym())
				desc.AddUint64(ctxt.Arch, 0)
			}
		}
	}
}

// 装载机部分
// 目前，此部分是在组装XCOFF文件时从头创建的
// 根据xfile对象中检索到的信息。

// 创建加载程序段并返回其大小
func Loaderblk(ctxt *Link, off uint64) {
	xfile.writeLdrScn(ctxt, off)
}

func (f *xcoffFile) writeLdrScn(ctxt *Link, globalOff uint64) {
	var symtab []*XcoffLdSym64
	var strtab []*XcoffLdStr64
	var importtab []*XcoffLdImportFile64
	var reloctab []*XcoffLdRel64
	var dynimpreloc []*XcoffLdRel64

	// 当字符串表在任何加载器小节中更新时，
	// 必须同时计算其长度。
	stlen := uint32(0)

	// 装载机收割台
	hdr := &XcoffLdHdr64{
		Lversion: 2,
		Lsymoff:  LDHDRSZ_64,
	}

	ldr := ctxt.loader
	/* Symbol table */
	for _, s := range f.loaderSymbols {
		lds := &XcoffLdSym64{
			Loffset: uint32(stlen + 2),
			Lsmtype: s.smtype,
			Lsmclas: s.smclas,
		}
		sym := s.sym
		switch s.smtype {
		default:
			ldr.Errorf(sym, "unexpected loader symbol type: 0x%x", s.smtype)
		case XTY_ENT | XTY_SD:
			lds.Lvalue = uint64(ldr.SymValue(sym))
			lds.Lscnum = f.getXCOFFscnum(ldr.SymSect(sym))
		case XTY_IMP:
			lds.Lifile = int32(f.dynLibraries[ldr.SymDynimplib(sym)] + 1)
		}
		ldstr := &XcoffLdStr64{
			size: uint16(len(ldr.SymName(sym)) + 1), // +零终止符
			name: ldr.SymName(sym),
		}
		stlen += uint32(2 + ldstr.size) // 2=ldstr.size的大小
		symtab = append(symtab, lds)
		strtab = append(strtab, ldstr)

	}

	hdr.Lnsyms = int32(len(symtab))
	hdr.Lrldoff = hdr.Lsymoff + uint64(24*hdr.Lnsyms) // 24=一个符号的大小
	off := hdr.Lrldoff                                // 当前偏移量与reloc偏移量相同

	/* Reloc */
	// 确保确定性秩序
	sort.Slice(f.loaderReloc, func(i, j int) bool {
		r1, r2 := f.loaderReloc[i], f.loaderReloc[j]
		if r1.sym != r2.sym {
			return r1.sym < r2.sym
		}
		if r1.roff != r2.roff {
			return r1.roff < r2.roff
		}
		if r1.rtype != r2.rtype {
			return r1.rtype < r2.rtype
		}
		return r1.symndx < r2.symndx
	})

	ep := ldr.Lookup(*flagEntrySymbol, 0)
	xldr := &XcoffLdRel64{
		Lvaddr:  uint64(ldr.SymValue(ep)),
		Lrtype:  0x3F00,
		Lrsecnm: f.getXCOFFscnum(ldr.SymSect(ep)),
		Lsymndx: 0,
	}
	off += 16
	reloctab = append(reloctab, xldr)

	off += uint64(16 * len(f.loaderReloc))
	for _, r := range f.loaderReloc {
		symp := r.sym
		if symp == 0 {
			panic("unexpected 0 sym value")
		}
		xldr = &XcoffLdRel64{
			Lvaddr:  uint64(ldr.SymValue(symp) + int64(r.roff)),
			Lrtype:  r.rtype,
			Lsymndx: r.symndx,
		}

		if ldr.SymSect(symp) != nil {
			xldr.Lrsecnm = f.getXCOFFscnum(ldr.SymSect(symp))
		}

		reloctab = append(reloctab, xldr)
	}

	off += uint64(16 * len(dynimpreloc))
	reloctab = append(reloctab, dynimpreloc...)

	hdr.Lnreloc = int32(len(reloctab))
	hdr.Limpoff = off

	/* Import */
	// 默认导入：/usr/lib:/lib
	ldimpf := &XcoffLdImportFile64{
		Limpidpath: "/usr/lib:/lib",
	}
	off += uint64(len(ldimpf.Limpidpath) + len(ldimpf.Limpidbase) + len(ldimpf.Limpidmem) + 3) // +空分隔符
	importtab = append(importtab, ldimpf)

	// adddynimpsym创建的映射将名称与数字相关联
	// 此数字表示此导入文件部分中的库索引（-1）
	// 因此，它们必须在放入区段之前进行排序
	libsOrdered := make([]string, len(f.dynLibraries))
	for key, val := range f.dynLibraries {
		if libsOrdered[val] != "" {
			continue
		}
		libsOrdered[val] = key
	}

	for _, lib := range libsOrdered {
		// lib字符串定义为base.a/mem.o或path/base.a/mem.o
		n := strings.Split(lib, "/")
		path := ""
		base := n[len(n)-2]
		mem := n[len(n)-1]
		if len(n) > 2 {
			path = lib[:len(lib)-len(base)-len(mem)-2]

		}
		ldimpf = &XcoffLdImportFile64{
			Limpidpath: path,
			Limpidbase: base,
			Limpidmem:  mem,
		}
		off += uint64(len(ldimpf.Limpidpath) + len(ldimpf.Limpidbase) + len(ldimpf.Limpidmem) + 3) // +空分隔符
		importtab = append(importtab, ldimpf)
	}

	hdr.Lnimpid = int32(len(importtab))
	hdr.Listlen = uint32(off - hdr.Limpoff)
	hdr.Lstoff = off
	hdr.Lstlen = stlen

	/* Writing */
	ctxt.Out.SeekSet(int64(globalOff))
	binary.Write(ctxt.Out, ctxt.Arch.ByteOrder, hdr)

	for _, s := range symtab {
		binary.Write(ctxt.Out, ctxt.Arch.ByteOrder, s)

	}
	for _, r := range reloctab {
		binary.Write(ctxt.Out, ctxt.Arch.ByteOrder, r)
	}
	for _, f := range importtab {
		ctxt.Out.WriteString(f.Limpidpath)
		ctxt.Out.Write8(0)
		ctxt.Out.WriteString(f.Limpidbase)
		ctxt.Out.Write8(0)
		ctxt.Out.WriteString(f.Limpidmem)
		ctxt.Out.Write8(0)
	}
	for _, s := range strtab {
		ctxt.Out.Write16(s.size)
		ctxt.Out.WriteString(s.name)
		ctxt.Out.Write8(0) // 零终止符
	}

	f.loaderSize = off + uint64(stlen)
}

// XCOFF汇编和写入文件

func (f *xcoffFile) writeFileHeader(ctxt *Link) {
	// 文件头
	f.xfhdr.Fmagic = U64_TOCMAGIC
	f.xfhdr.Fnscns = uint16(len(f.sections))
	f.xfhdr.Ftimedat = 0

	if !*FlagS {
		f.xfhdr.Fsymptr = uint64(f.symtabOffset)
		f.xfhdr.Fnsyms = int32(f.symbolCount)
	}

	if ctxt.BuildMode == BuildModeExe && ctxt.LinkMode == LinkInternal {
		ldr := ctxt.loader
		f.xfhdr.Fopthdr = AOUTHSZ_EXEC64
		f.xfhdr.Fflags = F_EXEC

		// 辅助集管
		f.xahdr.Ovstamp = 1 // 基于dump-o
		f.xahdr.Omagic = 0x10b
		copy(f.xahdr.Omodtype[:], "1L")
		entry := ldr.Lookup(*flagEntrySymbol, 0)
		f.xahdr.Oentry = uint64(ldr.SymValue(entry))
		f.xahdr.Osnentry = f.getXCOFFscnum(ldr.SymSect(entry))
		toc := ldr.Lookup("TOC", 0)
		f.xahdr.Otoc = uint64(ldr.SymValue(toc))
		f.xahdr.Osntoc = f.getXCOFFscnum(ldr.SymSect(toc))

		f.xahdr.Oalgntext = int16(logBase2(int(XCOFFSECTALIGN)))
		f.xahdr.Oalgndata = 0x5

		binary.Write(ctxt.Out, binary.BigEndian, &f.xfhdr)
		binary.Write(ctxt.Out, binary.BigEndian, &f.xahdr)
	} else {
		f.xfhdr.Fopthdr = 0
		binary.Write(ctxt.Out, binary.BigEndian, &f.xfhdr)
	}

}

func xcoffwrite(ctxt *Link) {
	ctxt.Out.SeekSet(0)

	xfile.writeFileHeader(ctxt)

	for _, sect := range xfile.sections {
		sect.write(ctxt)
	}
}

// 生成XCOFF程序集文件
func asmbXcoff(ctxt *Link) {
	ctxt.Out.SeekSet(0)
	fileoff := int64(Segdwarf.Fileoff + Segdwarf.Filelen)
	fileoff = int64(Rnd(int64(fileoff), int64(*FlagRound)))

	xfile.sectNameToScnum = make(map[string]int16)

	// 添加节
	s := xfile.addSection(".text", Segtext.Vaddr, Segtext.Length, Segtext.Fileoff, STYP_TEXT)
	xfile.xahdr.Otextstart = s.Svaddr
	xfile.xahdr.Osntext = xfile.sectNameToScnum[".text"]
	xfile.xahdr.Otsize = s.Ssize
	xfile.sectText = s

	segdataVaddr := Segdata.Vaddr
	segdataFilelen := Segdata.Filelen
	segdataFileoff := Segdata.Fileoff
	segbssFilelen := Segdata.Length - Segdata.Filelen
	if len(Segrelrodata.Sections) > 0 {
		// 将relro段合并到数据段作为
		// relro数据位于AIX上的数据段内。
		segdataVaddr = Segrelrodata.Vaddr
		segdataFileoff = Segrelrodata.Fileoff
		segdataFilelen = Segdata.Vaddr + Segdata.Filelen - Segrelrodata.Vaddr
	}

	s = xfile.addSection(".data", segdataVaddr, segdataFilelen, segdataFileoff, STYP_DATA)
	xfile.xahdr.Odatastart = s.Svaddr
	xfile.xahdr.Osndata = xfile.sectNameToScnum[".data"]
	xfile.xahdr.Odsize = s.Ssize
	xfile.sectData = s

	s = xfile.addSection(".bss", segdataVaddr+segdataFilelen, segbssFilelen, 0, STYP_BSS)
	xfile.xahdr.Osnbss = xfile.sectNameToScnum[".bss"]
	xfile.xahdr.Obsize = s.Ssize
	xfile.sectBss = s

	if ctxt.LinkMode == LinkExternal {
		var tbss *sym.Section
		for _, s := range Segdata.Sections {
			if s.Name == ".tbss" {
				tbss = s
				break
			}
		}
		s = xfile.addSection(".tbss", tbss.Vaddr, tbss.Length, 0, STYP_TBSS)
	}

	// 添加矮小部分
	for _, sect := range Segdwarf.Sections {
		xfile.addDwarfSection(sect)
	}

	// 添加并编写其余部分
	if ctxt.LinkMode == LinkInternal {
		// 装载机部分
		if ctxt.BuildMode == BuildModeExe {
			Loaderblk(ctxt, uint64(fileoff))
			s = xfile.addSection(".loader", 0, xfile.loaderSize, uint64(fileoff), STYP_LOADER)
			xfile.xahdr.Osnloader = xfile.sectNameToScnum[".loader"]

			// 更新符号表的文件关闭
			fileoff += int64(xfile.loaderSize)
		}
	}

	// 创建符号表
	xfile.asmaixsym(ctxt)

	if ctxt.LinkMode == LinkExternal {
		xfile.emitRelocations(ctxt, fileoff)
	}

	// 写符号表
	xfile.symtabOffset = ctxt.Out.Offset()
	for _, s := range xfile.symtabSym {
		binary.Write(ctxt.Out, ctxt.Arch.ByteOrder, s)
	}
	// 写字符串表
	xfile.stringTable.write(ctxt.Out)

	// 写标题
	xcoffwrite(ctxt)
}

// emitRelocations在外部链接中为go.o发出重新定位条目。
func (f *xcoffFile) emitRelocations(ctxt *Link, fileoff int64) {
	ctxt.Out.SeekSet(fileoff)
	for ctxt.Out.Offset()&7 != 0 {
		ctxt.Out.Write8(0)
	}

	ldr := ctxt.loader
	// relocsect从sect中的第一个重新定位符号，并返回
	// 发出的重新定位的总数。
	relocsect := func(sect *sym.Section, syms []loader.Sym, base uint64) uint32 {
		// ctxt.Logf（“%s 0x%x\n”，sect.Name，sect.Vaddr）
		// 如果主段没有位，则无需重新定位。
		if sect.Vaddr >= sect.Seg.Vaddr+sect.Seg.Filelen {
			return 0
		}
		sect.Reloff = uint64(ctxt.Out.Offset())
		for i, s := range syms {
			if !ldr.AttrReachable(s) {
				continue
			}
			if uint64(ldr.SymValue(s)) >= sect.Vaddr {
				syms = syms[i:]
				break
			}
		}
		eaddr := int64(sect.Vaddr + sect.Length)
		for _, s := range syms {
			if !ldr.AttrReachable(s) {
				continue
			}
			if ldr.SymValue(s) >= int64(eaddr) {
				break
			}

			// 在运行中计算外部重新定位，并传递到Xcoffreloc1以流出。
			// 重新定位必须按地址排序，因此创建一个排序索引列表。
			relocs := ldr.Relocs(s)
			sorted := make([]int, relocs.Count())
			for i := 0; i < relocs.Count(); i++ {
				sorted[i] = i
			}
			sort.Slice(sorted, func(i, j int) bool {
				return relocs.At(sorted[i]).Off() < relocs.At(sorted[j]).Off()
			})

			for _, ri := range sorted {
				r := relocs.At(ri)
				rr, ok := extreloc(ctxt, ldr, s, r)
				if !ok {
					continue
				}
				if rr.Xsym == 0 {
					ldr.Errorf(s, "missing xsym in relocation")
					continue
				}
				if ldr.SymDynid(rr.Xsym) < 0 {
					ldr.Errorf(s, "reloc %s to non-coff symbol %s (outer=%s) %d %d", r.Type(), ldr.SymName(r.Sym()), ldr.SymName(rr.Xsym), ldr.SymType(r.Sym()), ldr.SymDynid(rr.Xsym))
				}
				if !thearch.Xcoffreloc1(ctxt.Arch, ctxt.Out, ldr, s, rr, int64(uint64(ldr.SymValue(s)+int64(r.Off()))-base)) {
					ldr.Errorf(s, "unsupported obj reloc %d(%s)/%d to %s", r.Type(), r.Type(), r.Siz(), ldr.SymName(r.Sym()))
				}
			}
		}
		sect.Rellen = uint64(ctxt.Out.Offset()) - sect.Reloff
		return uint32(sect.Rellen) / RELSZ_64
	}
	sects := []struct {
		xcoffSect *XcoffScnHdr64
		segs      []*sym.Segment
	}{
		{f.sectText, []*sym.Segment{&Segtext}},
		{f.sectData, []*sym.Segment{&Segrelrodata, &Segdata}},
	}
	for _, s := range sects {
		s.xcoffSect.Srelptr = uint64(ctxt.Out.Offset())
		n := uint32(0)
		for _, seg := range s.segs {
			for _, sect := range seg.Sections {
				if sect.Name == ".text" {
					n += relocsect(sect, ctxt.Textp, 0)
				} else {
					n += relocsect(sect, ctxt.datap, 0)
				}
			}
		}
		s.xcoffSect.Snreloc += n
	}

dwarfLoop:
	for i := 0; i < len(Segdwarf.Sections); i++ {
		sect := Segdwarf.Sections[i]
		si := dwarfp[i]
		if si.secSym() != loader.Sym(sect.Sym) ||
			ldr.SymSect(si.secSym()) != sect {
			panic("inconsistency between dwarfp and Segdwarf")
		}
		for _, xcoffSect := range f.sections {
			_, subtyp := xcoffGetDwarfSubtype(sect.Name)
			if xcoffSect.Sflags&0xF0000 == subtyp {
				xcoffSect.Srelptr = uint64(ctxt.Out.Offset())
				xcoffSect.Snreloc = relocsect(sect, si.syms, sect.Vaddr)
				continue dwarfLoop
			}
		}
		Errorf(nil, "emitRelocations: could not find %q section", sect.Name)
	}
}

// xcoffCreateExportFile使用导出的符号为
// -Wl，-bE选项。
// 除非符号列在导出文件中，否则ld不会导出符号。
func xcoffCreateExportFile(ctxt *Link) (fname string) {
	fname = filepath.Join(*flagTmpdir, "export_file.exp")
	var buf bytes.Buffer

	ldr := ctxt.loader
	for s, nsym := loader.Sym(1), loader.Sym(ldr.NSym()); s < nsym; s++ {
		if !ldr.AttrCgoExport(s) {
			continue
		}
		extname := ldr.SymExtname(s)
		if !strings.HasPrefix(extname, "._cgoexp_") {
			continue
		}
		if ldr.IsFileLocal(s) {
			continue // 仅导出非静态符号
		}

		// 检索初始符号的名称
		// 由cgo出口。
		// 相应的Go符号为：
		// _cgoexp\u hashcode\u symname。
		name := strings.SplitN(extname, "_", 4)[3]

		buf.Write([]byte(name + "\n"))
	}

	err := ioutil.WriteFile(fname, buf.Bytes(), 0666)
	if err != nil {
		Errorf(nil, "WriteFile %s failed: %v", fname, err)
	}

	return fname
}
